
import TalkShow from "../Label/TalkShow";
import GameCtrl from "./GameCtrl";
import { z } from "./z";


const { ccclass, property } = cc._decorator;

/**组件类型 */
export enum E_StepType { 说话, 区域触发, 点击触发, 目标, 时间, 并行主事件, 并行子事件, }

/**游戏类型 */
export enum E_GameType { 进行, 开始, 正常结束, 失败, }

@ccclass(`EventSet`)
class EventSet {
    @property({ displayName: "开始后显示", type: [cc.Node], tooltip: `激活指定节点(active)` })
    startShowNodes: cc.Node[] = [];

    @property({ displayName: "开始后隐藏", type: [cc.Node], tooltip: `隐藏指定节点（active）` })
    startHiddenNodes: cc.Node[] = [];

    @property({ displayName: "完成后显示", type: [cc.Node], tooltip: `激活指定节点(active)` })
    finishShowNodes: cc.Node[] = [];

    @property({ displayName: "完成后隐藏", type: [cc.Node], tooltip: `隐藏指定节点（active）` })
    finishHiddenNodes: cc.Node[] = [];

    @property({ type: cc.Component.EventHandler, tooltip: "事件开始后回调", displayName: "开始后回调" })
    start_event_do: cc.Component.EventHandler[] = [];

    @property({ type: cc.Component.EventHandler, tooltip: "事件完成后回调", displayName: "完成后回调" })
    comp_events_do: cc.Component.EventHandler[] = [];
}

@ccclass(`NodeActive`)
class NodeListShow {
    @property({
        displayName: "执行到时激活节点",
        tooltip: `脚本被执行到该节点开启（active）`
    })
    public autoActiveSelf: boolean = true;

    @property({
        displayName: "执行到时显示节点",
        tooltip: `脚本被执行到该节点显示(opacity)`
    })
    public autoShowSelf: boolean = true;

    @property({
        displayName: "完成后关闭本节点",
        tooltip: `完成后自动关闭本节点（active）`
    })
    public closeOnFinish: boolean = false;

    @property({
        displayName: "完成后隐藏本节点",
        tooltip: `完成后隐藏本节点(opacity)`
    })
    public hideOnFinish: boolean = false;

    @property({
        displayName: "完成后关闭检测区域",
        tooltip: `完成后关闭触碰区域`,
        visible(this: NodeListShow) { return this.stepType == E_StepType.区域触发; }
    })
    public closeTriggerArea: boolean = true;

    @property({
        displayName: "完成后关闭并行子节点",
        tooltip: `完成后自动关闭并行子节点（active）`,
        visible(this: NodeListShow) {
            return this.gameType != E_GameType.失败 && this.gameType != E_GameType.正常结束 &&
                this.stepType == E_StepType.并行主事件;
        }
    })
    public closeParalleOnFinish: boolean = false;

    @property({
        displayName: "完成后隐藏并行子节点",
        tooltip: `完成后隐藏并行子节点(透明度)`,
        visible(this: NodeListShow) {
            return this.gameType != E_GameType.失败 && this.gameType != E_GameType.正常结束 &&
                this.stepType == E_StepType.并行主事件;
        }
    })
    public hideParalleOnFinish: boolean = false;

    gameType: E_GameType;
    stepType: E_StepType;

    public static init(step: EventStepCtrl) {
        let nodeContrl = new NodeListShow();
        nodeContrl.gameType = step.gameType;
        nodeContrl.stepType = step.stepType;
        return nodeContrl;
    }
}


/**碰撞类型 */
type ColliderObj = cc.BoxCollider & cc.PhysicsCollider & cc.CircleCollider;

@ccclass
export class EventStepCtrl extends cc.Component {
    /**开始step */
    private static startStep: number = 0;
    /**结束的step */
    private static endStep: number = 0;
    /**失败的step */
    private static faildStep: number = 0;
    /**进行中的step */
    private static conduckStep: number = 0;

    /**是否可以继续 */
    private static isCanContinue: boolean = true;

    public get gameType() { return this._gameType; }
    @property({
        type: cc.Enum(E_GameType),
        displayName: "游戏状态",
    })
    public set gameType(g: E_GameType) {
        this._gameType = g;
        this.nodeShowContrl.gameType = g;  //更新
    }
    @property
    private _gameType = E_GameType.进行;

    @property({
        type: cc.Enum(E_StepType),
        displayName: "组件类型",
        visible(this: EventStepCtrl) { return this.gameType != E_GameType.失败 && this.gameType != E_GameType.正常结束; }
    })
    public get stepType() { return this._stepType; }
    public set stepType(s: E_StepType) {
        this._stepType = s;
        this.nodeShowContrl.stepType = s;//更新内部状态
    }
    @property
    private _stepType = E_StepType.区域触发;


    @property({
        displayName: "下一步事件",
        type: EventStepCtrl,
        visible(this: EventStepCtrl) { return this.gameType != E_GameType.失败 && this.gameType != E_GameType.正常结束; }
    })
    public nextStep: EventStepCtrl = null;

    /**并行事件 */
    @property({
        displayName: "并行运行事件",
        type: EventStepCtrl,
        visible(this: EventStepCtrl) {
            return this.gameType != E_GameType.失败 && this.gameType != E_GameType.正常结束 &&
                this.stepType == E_StepType.并行主事件;
        }
    })
    public parallelEvents: EventStepCtrl[] = [];

    @property({
        displayName: "脚本完成时间",
        min: 0,
        visible(this: EventStepCtrl) {
            return this.gameType != E_GameType.失败 && this.gameType != E_GameType.正常结束 &&
                this.stepType == E_StepType.时间;
        }
    })
    public autoFinishDuration: number = 10;

    @property({
        displayName: "失败真言",
        visible(this: EventStepCtrl) { return this.gameType == E_GameType.失败; }
    })
    public failDes = "";

    @property({
        displayName: "目标文本",
        tooltip: `显示当前目标的文本`,
        visible(this: EventStepCtrl) {
            return this.gameType != E_GameType.失败 && this.gameType != E_GameType.正常结束 &&
                this.stepType == E_StepType.目标;
        }
    })
    public targetDes: string = '';

    @property({
        type: TalkShow,
        displayName: "聊天对话框",
        visible(this: EventStepCtrl) {
            return this.stepType == E_StepType.说话 && this.gameType != E_GameType.失败
                && this.gameType != E_GameType.正常结束;
        }
    })
    public currentTalk: TalkShow = null;

    @property({
        type: cc.Node,
        displayName: "检测区域节点",
        tooltip: `在本节点上挂在box2d组件检测`,
        visible(this: EventStepCtrl) {
            return this.gameType != E_GameType.失败 && this.gameType != E_GameType.正常结束 &&
                this.stepType == E_StepType.区域触发;
        }
    })
    public checkAearNodes: cc.Node = null;

    @property({
        displayName: "等待子事件完成",
        tooltip: `是否等待子事件执行完成后继续，只要由意见完成即可继续`,
        visible(this: EventStepCtrl) {
            return this.gameType != E_GameType.失败 && this.gameType != E_GameType.正常结束 &&
                this.stepType == E_StepType.并行主事件;
        }
    })
    public isWaitParallelFinish: boolean = true;

    @property({
        displayName: "等待所有子事件",
        tooltip: `是否等到所有子事件执行完成后继续`,
        visible(this: EventStepCtrl) {
            return this.gameType != E_GameType.失败 && this.gameType != E_GameType.正常结束 &&
                this.stepType == E_StepType.并行主事件 && this.isWaitParallelFinish;
        }
    })
    public isWaitParallelFinishAll: boolean = false;

    @property({
        type: NodeListShow,
        displayName: `节点控制`,
    })
    public nodeShowContrl: NodeListShow = NodeListShow.init(this);

    @property({
        type: EventSet,
        displayName: `回调事件集合`
    })
    public eventList: EventSet = new EventSet();

    @property
    private _NextEventPreview = false;

    @property({
        displayName: `开启下一步事件预览`,
        tooltip: `在编辑器里开启该事件后续的执行步骤预览`,
        visible(this: EventStepCtrl) { return this.gameType != E_GameType.失败 && this.gameType != E_GameType.正常结束; }
    })
    private set nextEventPreview(value: boolean) {
        this._NextEventPreview = value;
        //刷新列表
        let nextStep = this.nextStep;
        this.nextPreviewList = [];
        while (nextStep) {
            this.nextPreviewList.push(nextStep);
            if (nextStep.nextStep == null) break;
            nextStep = nextStep.nextStep;
        }
    }
    private get nextEventPreview() { return this._NextEventPreview; }

    @property({
        displayName: `下一步`,
        type: EventStepCtrl,
        readonly: true,
        visible(this: EventStepCtrl) { return this.nextEventPreview && this.gameType != E_GameType.失败 && this.gameType != E_GameType.正常结束; }
    })
    private nextPreviewList: EventStepCtrl[] = [];


    // @property({
    //     displayName: "显示后主角朝向",
    // })
    // public playerDirectOnShow: number = -1;
    // @property({
    //     displayName: "移动主角",
    //     visible(this: EventStepCtrl) { return this.gameType != GameType.GameTypeGameOver; }
    // })
    // public canTouchCtrlPlayer: boolean = false;


    // @property({
    //     displayName: "新手引导提示",
    //     type: cc.Node,
    //     visible(this: EventStepCtrl) { return this.gameType != GameType.GameTypeGameOver; }
    // })
    // public guideNode: cc.Node = null;

    /**完成事件的回调 */
    private finishCall = ()=>{};

    /**脚本逻辑层级index */
    public indexLevel: number = 0;

    /**该step是否完成 */
    private isFinish: boolean = false;

    onEnable() {
        if (this.stepType == E_StepType.点击触发) {
            //注册点击事件
            this.node.once(cc.Node.EventType.TOUCH_START, this.finishCall, this);
        }
    }

    public isGameStartStep(): boolean { return (this.gameType == E_GameType.开始); }
    public isGameOverStep(): boolean { return (this.gameType == E_GameType.正常结束); }
    public isGameFailStep(): boolean { return (this.gameType == E_GameType.失败); }

    /**
     *  根据相应的step设置层级--用于打印log
     * @param currentStep 要设置的层级的节点
     */
    public static setStepLevelIndex(currentStep: EventStepCtrl) {
        //遍历设置逻辑层级
        let setLevelIndex = (step: EventStepCtrl, level: number) => {
            if (is.null(step) || is.undefined(step)) return;

            step.indexLevel = level;
            step.parallelEvents.forEach(parallel => setLevelIndex(parallel, level + 1));
            is.not.null(step.nextStep) && setLevelIndex(step.nextStep, level);
        };
        setLevelIndex(currentStep, 0);
    }

    /**执行步骤 */
    public async showStep() {
        if (!EventStepCtrl.isCanContinue) return;

        this.log(`开始执行事件`);
        if (this.stepType != E_StepType.并行子事件) z.ctl.currentStep = this;//保存主事件

        //开始显示和隐藏节点 
        this.log(`step类型：${E_StepType[this.stepType]}, ${this.nodeShowContrl.autoShowSelf && '自动显示opacity'},${this.nodeShowContrl.autoActiveSelf && '自动开启active'}`);
        this.nodeShowContrl.autoActiveSelf && (this.node.active = true);
        this.nodeShowContrl.autoShowSelf && (this.node.opacity = 255);
        this.eventList.startShowNodes.forEach(element => element.active = true);
        this.eventList.startHiddenNodes.forEach(element => element.active = false);

        //并行
        await this.getComPromise();  //处理并行事件
        this.finishStep();//执行完成调用

        //判断是否结束事件
        if (this.gameType == E_GameType.失败) {
            this.log(`流程失败`);
            EventStepCtrl.isCanContinue = false;
            this.log(`失败真言：${this.failDes}`);
            return;
        } else if (this.gameType == E_GameType.正常结束) {
            this.log(`流程结束`);
            EventStepCtrl.isCanContinue = false;
            return;
        }

        this.log(`开始执行下一个事件`);
        await this.nextStep?.showStep(); //执行下一步
    }

    public getComPromise() {
        //设置事件执行
        this.log(`处理事件，事件执行状态：${E_GameType[this.gameType]}, 执行类型：${E_StepType[this.stepType]};`);
        let promis = new Promise<void>(resolve => {
            if (this.stepType == E_StepType.并行主事件) {
                let promisList: Promise<any>[] = [];
                let waitPromise: Promise<any>;
                //并行执行步骤
                this.parallelEvents.forEach(async step => promisList.push(step.showStep()));

                this.log(`执行并行逻辑事件，${this.isWaitParallelFinish ?
                    this.isWaitParallelFinishAll ? '等待所有子事件' : '等待子事件完成'
                    : '继续执行不等待'}`);
                //判断是否等待并行执行完成1个以上再继续
                if (this.isWaitParallelFinish && EventStepCtrl.isCanContinue) {
                    //判断并行完成还是单独完成
                    if (this.isWaitParallelFinishAll) {
                        waitPromise = Promise.all(promisList);
                    } else {
                        waitPromise = Promise.race(promisList);
                    }
                    //等待子事件完成
                    waitPromise.then(() => {
                        this.isWaitParallelFinish && EventStepCtrl.isCanContinue && resolve();
                    });
                } else if (EventStepCtrl.isCanContinue) return resolve();
            }

            this.log(`执行子事件`, this.stepType == E_StepType.并行子事件);

            //正常结束or 失败直接返回
            if (this.gameType == E_GameType.失败 || this.gameType == E_GameType.正常结束)
                EventStepCtrl.isCanContinue && resolve();

            //--计时或触发区域继续执行--触发结束回调
            this.finishCall = () => EventStepCtrl.isCanContinue && resolve();

            //时间类型设置计时---说话类型--待设计
            if (this.stepType == E_StepType.说话 || this.stepType == E_StepType.时间)
                this.scheduleOnce(this.finishCall, this.autoFinishDuration);
        });
        return promis;
    }
    /**
     * 当碰撞产生的时候调用
     * @param  {Collider} other 产生碰撞的另一个碰撞组件
     * @param  {Collider} self  产生碰撞的自身的碰撞组件
     */
    onCollisionEnter(other: ColliderObj, self: ColliderObj) {
        if (this.isFinish && this.nodeShowContrl.closeTriggerArea) return; //避免重复调用
        this.log(`触发碰撞区域`);
        this.finishCall();
    }

    /**
     *  打印step Log 
     * @param str 打印log
     * @param isLog 是否打印log
     */
    public log(str: string, isLog = true) {
        isLog && cc.log(`${'---'.repeat(this.indexLevel)}【${this.node.name}】:${str}`);
    }


    /** 完成事件 */
    public  finishStep(): void {
        this.log(`完成事件`);
        this.isFinish = true;

        this.currentTalk?.finishTalk(); //完成说话
        //显示和隐藏节点-完成回调
        this.eventList.finishHiddenNodes.forEach(element => element.active = false);
        this.eventList.finishShowNodes.forEach(element => element.active = true);
        this.eventList.comp_events_do.forEach((event: cc.Component.EventHandler) => {
            event.emit([event.customEventData]);
        });

        //完成事件后关闭并行执行的节点
        this.parallelEvents.forEach(element => {
            this.nodeShowContrl.hideParalleOnFinish && (element.node.opacity = 0);
            this.nodeShowContrl.closeParalleOnFinish && (element.node.active = false);
        });
        //关闭or隐藏本节点
        this.nodeShowContrl.closeOnFinish && (this.node.active = false);
        this.nodeShowContrl.hideOnFinish && (this.node.opacity = 0);
    }

    /**检测角色与 Collider 触发区域 */
    // public checkTriggerAear() {
    //     this.colliderList.forEach(collider => {
    //         let r1: cc.Rect = collider..getBoundingBoxToWorld();
    //         let r2: cc.Rect = z.player.footAearNode.getBoundingBoxToWorld();

    //         let rect = new cc.Rect();
    //         r1.intersection(rect, r2);
    //         if (rect.width > 0 && rect.height > 0) {
    //             //cc.log("dealChacterMove.enterShowNode。active=true");
    //             this.isAutoFinish = true;
    //             // this.enterShowNode.active = true;
    //             return true;
    //         }

    //     });
    // }


    public openGuid() {
        //开启引导
        // if (this.guideNode != null) {
        //     this.guideNode.active = true;
        //     let p1 = this.enterAearNode.parent.convertToWorldSpaceAR(this.enterAearNode.position);
        //     let p2 = this.guideNode.parent.convertToNodeSpaceAR(p1);
        //     this.guideNode.position = cc.v2(p2.x, p2.y);
        // }

    }

    public closeGuid() {

    }

    public stopPlayer() {

        //停止主角移动--并改变主角朝向
        // this.gmCtrl.scenMap.player.stop();
        // if (this.playerDirectOnShow >= 0) {
        // cc.log("showStep.changeDirect.playerDirectOnShow = " + this.playerDirectOnShow);
        // this.gmCtrl.scenMap.player.changeDirect(this.playerDirectOnShow);
        // }
    }

    public movePlayer() {

        //移动主角
        // if (this.stepType == StepType.StepTypePlayerAutoMove) {
        //     let pos = this.enterAearNode.parent.convertToWorldSpaceAR(this.enterAearNode.position);
        //     ctrl.scenMap.movePlayer(pos.x, pos.y);
        // }
    }

    public canTouchCtrlPlay() {
        // if (this.parallelEvents.length > 0) {
        //     // cc.log("canTouchCtrlPlay.this.runSameSteps.length = " + this.runSameSteps.length);

        //     for (let i = 0; i < this.parallelEvents.length; i++) {
        //         let element = this.parallelEvents[i];
        //         // if(element.nextStep && element.nextStep.node.active)
        //         // {
        //         //     if(!element.nextStep.canTouchCtrlPlay())
        //         //     {
        //         //         cc.log("canTouchCtrlPlay.1");
        //         //         return false;
        //         //     }
        //         // }

        //         if (!element.canTouchCtrlPlay()) {
        //             // cc.log("canTouchCtrlPlay.2");
        //             return false;
        //         }
        //     }
        // }

        // if (this.stepType == StepType.StepTypeTalk || this.stepType == StepType.StepTypePlayerAutoMove) {
        //     return false;
        // }
        // return this.canTouchCtrlPlayer;
    }

    public dealChacterMove(): boolean {
        // if (this.parallelEvents != null && this.parallelEvents.length > 0) {
        //     this.parallelEvents.forEach(element => {
        //         element.dealChacterMove();
        //     });
        // }

        // if (this.stepType != StepType.StepTypeEnterShow && this.stepType != StepType.StepTypePlayerAutoMove && this.stepType != StepType.StepTypePlayerClickMove) {
        //     return false;
        // }
        // if (!this.node.activeInHierarchy) {
        //     // || this.enterShowNode.activeInHierarchy
        //     return false;
        // }

        // let r1: cc.Rect = this.enterAearNode.getBoundingBoxToWorld();
        // let r2: cc.Rect = z.player.footAearNode.getBoundingBoxToWorld();

        // let rect = new cc.Rect();
        // r1.intersection(rect, r2);
        // if (rect.width > 0 && rect.height > 0) {
        //     //cc.log("dealChacterMove.enterShowNode。active=true");
        //     this.isAutoFinish = true;
        //     // this.enterShowNode.active = true;
        //     return true;
        // }

        // for (let i = 0; i < this.checkAearNodes.length; i++) {
        //     let r11: cc.Rect = this.checkAearNodes[i].getBoundingBoxToWorld();
        //     let rr1 = new cc.Rect();
        //     r11.intersection(rr1, r2);
        //     if (rr1.width > 0 && rr1.height > 0) {
        //         this.isAutoFinish = true;
        //         return true;
        //     }
        // }
        return false;
    }
}
